#pragma once

#include "IActiveObject.h"
#include "TimeValue.h" // for default param
#include "RoomTimer.h"
#include "RoomTimerThread.h"
#include "SimpleEvent.h"
#include "SimpleCriticalSection.h"
#include "EndpointMap.h"
#include "NullDeleter.h"
#include "Binding.h"

//
// Define ACTIVE_OBJECT_TIMER_THREADS to 1 to allow an active object to set
// more than ~60 timers at once.
//
#define ACTIVE_OBJECT_TIMER_THREADS 0

namespace RoomFW {

//
// Class:	ActiveObject
//
// Description:	Active Object base class. Derived classes must implement
//				specific functionality. 
//
// Author:	Andrew Sage
//
class ActiveObject;
typedef ::boost::shared_ptr<ActiveObject> ActiveObjectSP;
class ActiveObject : public virtual IActiveObject
{
public:

	//
	// Method:	ActiveObject
	//
	// Description:	Constructor
	//
	// Parameters:
	//
	//	portCount - Number of Port this object has.
	//
	//	portPriority - Must have portCount + 1 elements indicating the priority for each port index,
	//					including the timer port index at portCount.
	//
	ActiveObject(size_t portCount, const PortPriorityArray& portPriority);

	//
	// Method:	~ActiveObject
	//
	// Description:	Destructor
	//
	virtual ~ActiveObject();

	//
	// Message handler to allow more modular design.
	//
	class MessageHandler
	{
	public:
		//
		// Smart Pointer type.
		//
		typedef boost::shared_ptr<MessageHandler> Ptr;
		typedef NullDeleter<MessageHandler> NullDeleter;

		//
		// ctor.
		//
		MessageHandler(ActiveObject* parent);
		virtual ~MessageHandler() {}

		//
		// ActiveObject wrappers.
		//
		void sendMessage(const PortIndex& portIndex,
						 MessageID messageID,
						 IPayloadSP payload = IPayloadSP())
			throw (SendMessageError);
		RoomMessageSP invoke(uint32_t timeout,
							 const PortIndex& portIndex,
							 MessageID messageID,
							 IPayloadSP payload = IPayloadSP(),
							 bool throwOnTimeout = true) const
			throw (SendMessageError);
		RoomTimer::TimerId setTimer(const TimeValue& expireIn, const TimeValue& repeatEvery = 0);
		RoomTimer::TimerId setTimer(IPayloadSP payload, const TimeValue& expireIn, const TimeValue& repeatEvery = 0);
		bool cancelTimer(RoomTimer::TimerId& timerId);
		const char* getObjectName(void) const;
		const char* getPortName(size_t portIndex) const;
		IContractCheckerSPC getContractChecker(PortIndex portIndex) const;
		PortIndex::PrimaryIndex getTimerPort() const;
		bool registerDynamicPort(PortIndex::PrimaryIndex port, const char* dynamicPortName = 0, bool remote=false);
		bool registerRemoteDynamicPort(PortIndex::PrimaryIndex port, const char* dynamicPortName = 0);
		DynamicBindingSP dynamicBind(PortIndex::PrimaryIndex port, const char* dynamicPortName);
		size_t timerPortIndex() const;
		bool isBound(const PortIndex& portIndex) const;

		//
		// Handle message.
		//
		virtual bool handle(RoomMessageSP message) {return false;} // Concrete class can be used as sender.

		//
		// Error handling hook.  Called by default error handling macros.
		//
		virtual void onError(ErrorHandling::IError& e) {}

		//
		// Exception handling hook.  Called by default error handling macros.
		//
		virtual void onException(std::exception& e) {}

		//
		// Exception handling hook.  Called by default error handling macros.
		//
		virtual void onCatchAllException() {}

	protected:
		ActiveObject* getParent() const;

	private:
		ActiveObject* parent_;
	};

	//
	// Method:	mapMessageHandler
	//
	// Description:	Assign a message handler to handle messages for a port.
	//
	// Parameters:
	//
	//	portIndex -	Port index can be specific replication, primary index only, or PortIndex::INVALID for all ports.
	//
	//	handler -	Message handler to handle messages for portIndex.
	//
	void mapMessageHandler(const PortIndex& portIndex, MessageHandler::Ptr handler);

	//
	// Method:	unMapMessageHandler
	//
	// Description:	Remove message handler for specified port.
	//
	// Parameters:
	//
	//	portIndex -	Port index can be specific replication, primary index only, or PortIndex::INVALID for all ports.
	//
	//	handler -	Message handler to handle messages for portIndex.
	//
	void unMapMessageHandler(const PortIndex& portIndex, MessageHandler::Ptr handler);

	//
	// Method:	clearMessageHandlers
	//
	// Description:	Remove all message handlers.
	//
	// Parameters:	None.
	//
	void clearMessageHandlers();

	//
	// Method:	defaultDynamicPortName
	//
	// Description:	Generate the default name for registering a dynamic port.  [object name]_[port name].
	//
	// Parameters:	Port to generate name for.
	//
	std::string defaultDynamicPortName(PortIndex::PrimaryIndex port) const;

	//
	// IActiveObject overrides.
	//
	virtual IEndPointSP getEndPoint(PortIndex::PrimaryIndex portPrimaryIndex);
	virtual bool setEndPoint(PortIndex& portIndex, IEndPointSP endPoint);
	virtual bool clearEndPoint(const PortIndex& portIndex);
	virtual void put(RoomMessageSP message);

	//
	// Thread overrides.
	//
	virtual std::string errorMessage();
	virtual ErrorHandling::IErrorSP getError();
	virtual const char* getThreadName() const;

	//
	// Method to allow uniform usage between AO and MH.  Public so tests can get message info.
	//
	virtual size_t timerPortIndex() const;

protected:

	//
	// Thread overrides.
	//
	virtual bool initializeObject();
	virtual void main();
	virtual void signalStop();

	//
	// Method:	isBound
	//
	// Description:	Determines if a port is bound.
	//
	// Parameters:
	//
	//	portIndex -	Port index to check.  Can be specific replication or broadcast.
	//
	// Return:	True if the indicated port is bound.
	//
	bool isBound(const PortIndex& portIndex) const;

	//
	// Method:	replicationCount
	//
	// Description:	Determines how many times a port is replicated.
	//
	// Parameters:
	//
	//	port -	Primary index of the port to check.
	//
	// Return:	Replication count for specified port.
	//
	size_t replicationCount(PortIndex::PrimaryIndex port) const;

	//
	// Method:	getFirstBound
	//
	// Description:	Get the first bound replication of a specified port.  Returns broadcast replication if unbound.
	//				Best to check isBound() first.
	//
	// Parameters:
	//
	//	port -	Primary index of the port to check.
	//
	// Return:	First bound replication of a specified port.  Returns full PortIndex to pass to sendMessage().
	//
	PortIndex getFirstBound(PortIndex::PrimaryIndex port) const;

	//
	// Method:	getNextBound
	//
	// Description:	Get the next bound replication of a specified port.
	//
	// Parameters:
	//
	//	portIndex -	Current port index.  Replication incremented by call.
	//
	// Return:	True if the portIndex was updated with a valid port, false if there were no more bound.
	//
	bool getNextBound(PortIndex& portIndex) const;

	//
	// Method:	sendMessage
	//
	// Description:	Send a message from this class.  Available to derived classes.
	//
	// Parameters:
	//
	//	portIndex -	Index of port to send message out.
	//
	//	messageID -	Message ID.
	//
	//	payload -	Message payload.
	//
	// Return:	Success.  Failure could be unconnected port or endpoint refused message
	//			due to contract violation.
	//
	void sendMessage(const PortIndex& portIndex, MessageID messageID, IPayloadSP payload = IPayloadSP())
		throw (SendMessageError);

	//
	// Method:	invoke
	//
	// Description:	Send a message from this class and wait for a reply.  This creates a situation
	//				where ActiveObjects are blocking on each other which risks deadlock, priority
	//				inversion and the common cold.  Use only with extreme caution!
	//
	// Parameters:
	//
	//	timeout -	Max time in milliseconds to wait for a reply.  If the reply is not received in
	//				this time, it will be lost and a null message will be returned.  This must be
	//				checked and accouted for.  Under no circumstance should you assume that you will
	//				always get a reply.
	//
	//	portIndex -	Index of port to send message out.
	//
	//	messageID -	Message ID.
	//
	//	payload -	Message payload.
	//
	//	throwOnTimeout -	True to throw a SendMessageErrorSP on timeout.  Otherwise returns 0.
	//
	// Return:	Reply or null message.  Null message could be that the receiver didn't reply, or there
	//			was a contract violation sending.
	//
	RoomMessageSP invoke(uint32_t timeout,
						 const PortIndex& portIndex,
						 MessageID messageID,
						 IPayloadSP payload = IPayloadSP(),
						 bool throwOnTimeout = true) const
		throw (SendMessageError);

	//
	// Method:	handle
	//
	// Description:	Handle a message.  Derived classes may override or map message handlers instead.
	//
	// Parameters:
	//
	//	message - RoomMessage to handle.
	//
	// Return:	None
	//
	virtual void handle(RoomMessageSP message) {}

	//
	// Method:	onError
	//
	// Description:	Error handling hook.  Called by default error handling macros.
	//
	// Parameters:
	//
	//	e - Error caught.
	//
	// Return:	None
	//
	virtual void onError(ErrorHandling::IError& e) {}

	//
	// Method:	onException
	//
	// Description:	Exception handling hook.  Called by default error handling macros.
	//
	// Parameters:
	//
	//	e - Exception caught.
	//
	// Return:	None
	//
	virtual void onException(std::exception& e) {}

	//
	// Method:	onCatchAllException
	//
	// Description:	Exception handling hook.  Called by default error handling macros.
	//
	// Parameters:	None.
	//
	// Return:	None
	//
	virtual void onCatchAllException() {}

	//
	// Method:	setTimer
	//
	// Description:	Set a timer.  When the timer expires, a RoomMessage
	//				containing a TimerIdPayload will be sent to the timer port.
	//
	// Parameters:
	//
	//	expireIn - Relative time to first timer expiration.
	//
	//	repeatEvery -	Interval for repeat expirations.  If 0, the timer
	//					only expires once.
	//
	// Return:	Unique id of new timer or RoomTimer::InvalidTimerId if it fails.
	//
	RoomTimer::TimerId setTimer(const TimeValue& expireIn, const TimeValue& repeatEvery = 0);

	//
	// Method:	setTimer
	//
	// Description:	Set a timer.  When the timer expires, a RoomMessage
	//				containing the specified payload will be sent to the timer port.
	//
	// Parameters:
	//
	//	payload - Payload to send in expiration message.
	//
	//	expireIn - Relative time to first timer expiration.
	//
	//	repeatEvery -	Interval for repeat expirations.  If 0, the timer
	//					only expires once.
	//
	// Return:	Unique id of new timer or RoomTimer::InvalidTimerId if it fails.
	//
	RoomTimer::TimerId setTimer(IPayloadSP payload, const TimeValue& expireIn, const TimeValue& repeatEvery = 0);

	//
	// Method:	cancelTimer
	//
	// Description:	Cancel a timer.  Non-repeating timers do not need to be canceled
	//				after they expire.
	//
	// Parameters:
	//
	//	timerId - Timer id returned from setTimer().  Will be set to InvalidTimerId.
	//
	// Return:	Success.
	//
	bool cancelTimer(RoomTimer::TimerId& timerId);

	//
	// Method:	registerDynamicPort
	//
	// Description:	Register a port for dynamic binding by name.
	//
	// Parameters:
	//
	//	port - Port to register.
	//
	//	dynamicPortName - Name to register.  Pass in null for default of [object name]_[port name]
	//
	// Return:	Success.  Fails if name already registered.
	//
	bool registerDynamicPort(PortIndex::PrimaryIndex port, const char* dynamicPortName = 0, bool remote = false);
	bool registerRemoteDynamicPort(PortIndex::PrimaryIndex port, const char* dynamicPortName = 0);

	//
	// Method:	unregisterDynamicPorts
	//
	// Description:	Unregister all dynamic port for this object..
	//
	// Parameters:	None.
	//
	void unregisterDynamicPorts();

	//
	// Method:	dynamicBind
	//
	// Description:	Bind to a port that has been registered by name for dynamic binding.
	//
	// Parameters:
	//
	//	port - Port on this object to bind.
	//
	//	dynamicPortName - Name of port to bind to.
	//
	// Return:	DynamicBinding shared pointer.  Reset it to unbind.
	//
	DynamicBindingSP dynamicBind(PortIndex::PrimaryIndex port, const char* dynamicPortName);

	//
	// Method : messageQueueEmpty
	//
	// return : true if the message queue is empty
	//
	bool messageQueueEmpty();

	//
	// Timer port index.
	//
	const size_t timerPortIndex_;

	//
	// Overrides to allow derived objects to add extra events to wait on.
	//
	virtual size_t waitHandleSize() {return 0;} // Return the number of events you'll add with getWaitHandles()
	virtual void getWaitHandles(HandleArray& handles) {} // Add your extra events.
	virtual void handleEvent(HANDLE eventHandle) {} // Handle one of your extra events.

private:

	//
	// Decouple implementation from base class (maybe just a little).
	//
	typedef Thread SUPER;

	//
	// Check for errors before sending a message.
	//
	void preSend(PortIndex& portIndex, MessageID messageID, IPayloadSP& payload, bool isInvoke) const;

	//
	// Pass message to endpoint and throw errors.
	//
	void finalSend(const PortIndex& portIndex, MessageID messageID, IPayloadSP& payload, IEndPointSP replyEndpoint) const;

	//
	// Handle all message in queue.
	//
	void handleMessages();

	//
	// Get a message from the queue.
	//
	RoomMessageSP pop();

	//
	// Get or create an endpoint for replies..
	//
	IEndPointSP getReplyEndPoint(const PortIndex& portIndex);

	//
	// Endpoints for outgoing messages.
	//
	EndpointMap endpoints_;

	//
	// Endpoints for reply messages.
	//
	EndpointMap replyEndpoints_;

	//
	// Timer.
	//
	RoomTimer::IdMgr timerIdMgr_;
	RoomTimer timer_;
	typedef std::list<RoomTimerThreadSP> TimerThreadList;
#if ACTIVE_OBJECT_TIMER_THREADS
	TimerThreadList timerThreads_;
#endif

	//
	// Thread stop event.
	//
	SimpleEvent stopEvent_;

	//
	// Port Priorities.
	//
	PortPriorityArray portPriority_;

	//
	// Message queue.
	//
	typedef std::list<RoomMessageSP> MessageQueue;
	MessageQueue messageQueue_;

	//
	// New message event.
	//
	SimpleEvent queueEvent_;

	//
	// Message queue synchronization.
	//
	SimpleCriticalSection queueSynch_;

	//
	// Number of events this object directly owns.
	//
	static const size_t EventCount = 2;

	//
	// Message handler mapping.
	//
	typedef std::multimap<PortIndex, MessageHandler::Ptr> MessageHanderMap;
	MessageHanderMap messageHanderMap_;

	//
	// This class is not designed to be copied.
	//
private:
	ActiveObject(const ActiveObject&);
	ActiveObject& operator =(const ActiveObject&);

	//
	// Allow test suites to access to private data.
	//
	friend class TimerTestSuite;

	bool dynamicPortRegistered_;
};

}
